package com.learn.springframework.context;

import com.learn.springframework.annotation.Autowired;
import com.learn.springframework.annotation.Controller;
import com.learn.springframework.annotation.Service;
import com.learn.springframework.aop.DefaultAopProxyFactory;
import com.learn.springframework.aop.JdkDynamicAopProxy;
import com.learn.springframework.aop.config.AopConfig;
import com.learn.springframework.aop.support.AdvisedSupport;
import com.learn.springframework.bean.BeanWrapper;
import com.learn.springframework.bean.config.BeanDefinition;
import com.learn.springframework.bean.support.BeanDefinitionReader;
import com.learn.springframework.bean.support.DefaultListableBeanFactory;
import com.learn.springframework.core.BeanFactory;

import java.lang.reflect.Field;
import java.util.*;

/**
 * ApplicationContext上下文:持有BeanFactory引用、或者叫做门面模式
 *
 * @author MI
 */
public class ApplicationContext implements BeanFactory {

    private DefaultListableBeanFactory registry = new DefaultListableBeanFactory();

    private Set<String> singletonCurrentlyInCreation = new HashSet<>();

    /**
     * 一级缓存
     */
    private Map<String, Object> singletonObjects = new HashMap<>();
    /**
     * 二级缓存
     */
    private Map<String, Object> earlySingletonObjects = new HashMap<>();

    /**
     * 三级缓存
     */
    private Map<String, BeanWrapper> factoryBeanInstanceCache = new HashMap<String, BeanWrapper>();


    private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();

    private DefaultAopProxyFactory proxyFactory = new DefaultAopProxyFactory();

    private BeanDefinitionReader reader;

    public ApplicationContext(String... configLocations) {
        // 1、加载配置文件
        reader = new BeanDefinitionReader(configLocations);

        try {
            // 2、解析配置文件，将所有的配置信息封装成BeanDefinition对象
            List<BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            // 3、缓存所有配置信息
            this.registry.doRegistryBeanDefinition(beanDefinitions);
            // 4、加载非延迟加载的所有Bean
            doLoadInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void doLoadInstance() {
        // 循环调用getBean()方法
        for (Map.Entry<String, BeanDefinition> entry : this.registry.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            if (!entry.getValue().isLazyInit()) {
                // 最终创建对象由getBean触发
                getBean(beanName);
            }
        }

    }

    /**
     * 从IoC容器中获得一个对象
     *
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        // 1、先拿到BeanDefinition配置信息
        BeanDefinition beanDefinition = registry.beanDefinitionMap.get(beanName);

        Object singleton = getSingleton(beanName, beanDefinition);
        if (singleton != null) {
            return singleton;
        }
        // 标记Bean正在创建
        if (!singletonCurrentlyInCreation.contains(beanName)) {
            singletonCurrentlyInCreation.add(beanName);
        }
        // 2、反射实例化对象
        Object instance = instanceBean(beanName, beanDefinition);

        this.singletonObjects.put(beanName, instance);
        // 3、将返回的bean对象封装成BeanWrapper
        BeanWrapper beanWrapper = new BeanWrapper(instance);
        // 4、依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);
        // 5、保存IOC 容器
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);

        return beanWrapper.getWrappedInstance();
    }

    private Object getSingleton(String beanName, BeanDefinition beanDefinition) {
        // 先去一级缓存里面拿
        Object bean = singletonObjects.get(beanName);
        if (bean == null && singletonCurrentlyInCreation.contains(beanName)) {
            bean = earlySingletonObjects.get(beanName);
            // 如果二级缓存也没有，就去三级缓存去
            if (bean == null) {
                bean = instanceBean(beanName, beanDefinition);
                earlySingletonObjects.put(beanName, bean);
            }
        }

        return bean;
    }

    private void populateBean(String beanName, BeanDefinition beanDefinition, BeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrappedInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();
        if (!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))) {
            return;
        }
        try {
            // 忽略字段的修饰符、不管是private、protected、public、default,正常来讲，普通的OOP编程只能拿到public的属性
            for (Field field : clazz.getDeclaredFields()) {
                if (!field.isAnnotationPresent(Autowired.class)) {
                    continue;
                }
                Autowired autowired = field.getAnnotation(Autowired.class);
                // 如果用户没有自定义beanName,默认就是根据类型注入
                String autowireBeanName = autowired.value().trim();
                if ("".equals(autowireBeanName)) {
                    // 获得接口的类型，作为key待会拿到这个key到ioc容器中取值
                    autowireBeanName = field.getType().getName();
                }
                // 暴力访问，强吻
                field.setAccessible(true);

                field.set(instance, getBean(autowireBeanName));

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object instanceBean(String beanName, BeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();
            // 如果是代理对象，触发AOP的逻辑
            AdvisedSupport config = instantionAopConfig(beanDefinition);
            config.setTargetClass(clazz);
            config.setTarget(instance);
            /*判断规则，是否要生成代理，如果要调用工厂生成代理类，并且放入三级缓存*/
            // 如果不符合规则，返回原生类
            if (config.pointCutMatch()) {
                instance = proxyFactory.createAopProxy(config).getProxy();
            }

            this.factoryBeanObjectCache.put(beanName, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

//    private AdvisedSupport instantionAopConfig(BeanDefinition beanDefinition) {
//        AopConfig config = new AopConfig();
//        config.setPointCut(this.getConfig().getProperty("pointCut"));
//        config.setAspectClass(this.getConfig().getProperty("aspectClass"));
//        config.setAspectBefore(this.getConfig().getProperty("aspectBefore"));
//        config.setAspectAfter(this.getConfig().getProperty("aspectAfter"));
//        config.setAspectAfterThrow(this.getConfig().getProperty("aspectAfterThrow"));
//        config.setAspectAfterThrowingName(this.getConfig().getProperty("aspectAfterThrowingName"));
//        return new AdvisedSupport(config);
//
//    }

    private AdvisedSupport instantionAopConfig(BeanDefinition beanDefinition) {
        AopConfig config = new AopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new AdvisedSupport(config);

    }

    @Override
    public Object getBean(Class beanClass) {
        return getBean(beanClass.getName());
    }

    public int getBeanDefinitionCount() {
        return this.registry.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.registry.beanDefinitionMap.keySet().toArray(new String[0]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }
}
